package cn.edu.buaa.sei.jdat;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.HashSet;
import java.util.List;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantString;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.DescendingVisitor;
import org.apache.bcel.classfile.EmptyVisitor;
import org.apache.bcel.classfile.FieldOrMethod;
import org.apache.bcel.classfile.JavaClass;

import com.cocotingo.snail.dispatcher.DispatchQueue;

import cn.edu.buaa.sei.jdat.exception.InvalidURIException;
import cn.edu.buaa.sei.jdat.graph.DependencyGraph;
import cn.edu.buaa.sei.jdat.io.JarLoader;
import cn.edu.buaa.sei.jdat.model.Field;
import cn.edu.buaa.sei.jdat.model.IModifier;
import cn.edu.buaa.sei.jdat.model.Interface;
import cn.edu.buaa.sei.jdat.model.Jar;
import cn.edu.buaa.sei.jdat.model.Member;
import cn.edu.buaa.sei.jdat.model.Method;
import cn.edu.buaa.sei.jdat.model.Package;
import cn.edu.buaa.sei.jdat.model.Type;
import cn.edu.buaa.sei.jdat.model.Class;
import cn.edu.buaa.sei.jdat.model.BasicType;
import cn.edu.buaa.sei.jdat.model.TypeDelegator;

//
//
//  Generated by StarUML(tm) Java Add-In
//
//  @ Project : Jar Dependency Analysis Toolkit
//  @ File Name : JarController.java
//  @ Date : 2012/1/13
//  @ Author : 
//
//


public abstract class JarController {
	
	public static interface ProgressMonitor {
		void updateProgress(double progress);
		void finished();
	}
	
	private JarLoader loader = new JarLoader();

	private final JarCollection collection;
	private final List<Jar.StateChangeListener> stateChangeListeners;
	private final UnresolvedTable unresolvedTable;
	private final ResolvedTable resolvedTable;
	private final TypeDelegatorTable typeDelegatorTable;
	
	// Executes step 1 of importJar
	private final DispatchQueue queueDownloading = new DispatchQueue(4);
	// Executes step 2 and 3 of importJar
	private final DispatchQueue queueBuilding = new DispatchQueue(4);
	// Executes step 4 of importJar
	private final DispatchQueue queueAnalysis = new DispatchQueue();
	
	private final DispatchQueue queueImportJar = new DispatchQueue();
	
	
	/**
	 * This table records unresolved names of types. With each type name, there is a list of 
	 * types that refer to the type. When the type name is be transformed to instance of type, 
	 * this table will notify all types that refer to this type to update their references 
	 * list.
	 * @author Alven
	 *
	 */
	public static class UnresolvedTable {
		
		private Hashtable<String, ArrayList<Type>> table = new Hashtable<String, ArrayList<Type>>();
		
		public void addItem(String refName, Type type) {
			if (table.containsKey(refName)) {
				table.get(refName).add(type);
			} else {
				ArrayList<Type> array = new ArrayList<Type>();
				array.add(type);
				table.put(refName, array);
			}
		}
		
		public void removeItem(String refName, Type type) {
			if (table.containsKey(refName)) {
				ArrayList<Type> types = table.get(refName);
				types.remove(type);
				if (types.size() == 0)
					table.remove(refName);
			}
		}
		
		public void removeName(String refName) {
			table.remove(refName);
		}
		
		public List<Type> getTypes(String refName) {
			return table.get(refName);
		}
	}
	
	/**
	 * This table records resolved names of types. With each type name, there is a list of types
	 * that their signatures is same with the name. Namely, the table records types with the same
	 * names. The references to a type will only point to the first instance in same-name-types.
	 * When one type is removed, it's available with this table to check whether there 
	 * is another type with the same name, and if there is, all references to the removed type 
	 * will point to this same-name-type, and otherwise, the name will be added in unresolved table.
	 * @author Alven
	 *
	 */
	public static class ResolvedTable {
		private Hashtable<String, ArrayList<Type>> table = new Hashtable<String, ArrayList<Type>>();
		
		public void addType(Type type) {
			String name = type.getSignature();
			if (table.containsKey(name)) {
				table.get(name).add(type);
			} else {
				ArrayList<Type> array = new ArrayList<Type>();
				array.add(type);
				table.put(name, array);
			}
		}
		
		public void removeType(Type type) {
			String name = type.getSignature();
			if (table.containsKey(name)) {
				ArrayList<Type> array = table.get(name);
				array.remove(type);
				if (array.size() == 0)
					table.remove(name);
			}
		}
		
		public Type getType(String name) {
			return table.containsKey(name) ? table.get(name).get(0) : null;
		}
	}
	
	private static class TypeDelegatorTable {
		private Hashtable<String, TypeDelegator> table = new Hashtable<String, TypeDelegator>();
		
		public TypeDelegatorTable() {
			table.put("boolean", new TypeDelegator("boolean", BasicType.BOOLEAN));
			table.put("byte", new TypeDelegator("byte", BasicType.BYTE));
			table.put("char", new TypeDelegator("char", BasicType.CHAR));
			table.put("double", new TypeDelegator("double", BasicType.DOUBLE));
			table.put("float", new TypeDelegator("float", BasicType.FLOAT));
			table.put("int", new TypeDelegator("int", BasicType.INT));
			table.put("long", new TypeDelegator("long", BasicType.LONG));
			table.put("short", new TypeDelegator("short", BasicType.SHORT));
			table.put("void", new TypeDelegator("void", BasicType.VOID));
		}
		
		public synchronized void register(Field field, String typeName) {
			addTypeDelegator(typeName);
			TypeDelegator delegator = table.get(typeName);
			field.setTypeDelegator(delegator);
			delegator.alloc();
		}
		
		public synchronized void register(Method method, String retTypeName) {
			addTypeDelegator(retTypeName);
			TypeDelegator delegator = table.get(retTypeName);
			method.setReturnTypeDelegator(delegator);
			delegator.alloc();
		}
		
		public synchronized void register(Method method, String argTypeName, int loc) {
			addTypeDelegator(argTypeName);
			TypeDelegator delegator = table.get(argTypeName);
			method.setArgumentTypeDelegator(delegator, loc);
			delegator.alloc();
		}
		
		public void unregister(Field field) {
			String name = field.getTypeSignature();
			TypeDelegator delegator = table.get(name);
			delegator.dealloc();
			if (delegator.canRelease())
				table.remove(name);
		}
		
		public void unregister(Method method) {
			String returnName = method.getReturnTypeSignature();
			TypeDelegator delegator = table.get(returnName);
			delegator.dealloc();
			if (delegator.canRelease())
				table.remove(returnName);
			for (int i = 0; i < method.getArgumentSize(); i++) {
				String argName = method.getArgumentSignature(i);
				delegator = table.get(argName);
				delegator.dealloc();
				if (delegator.canRelease())
					table.remove(argName);
			}
		}
		
		public void addType(Type type) {
			TypeDelegator delegator = table.get(type.getSignature());
			if (delegator != null)
				delegator.setType(type);
		}
		
		public void removeType(Type type) {
			TypeDelegator delegator = table.get(type.getSignature());
			if (delegator != null)
				delegator.setType(null);
		}
		
		private void addTypeDelegator(String name) {
			if (table.containsKey(name))
				return;
			else
				table.put(name, new TypeDelegator(name));
		}
	}
	
	public JarController() {
		collection = new JarCollection();
		stateChangeListeners = Collections.synchronizedList(new ArrayList<Jar.StateChangeListener>());
		unresolvedTable = new UnresolvedTable();
		resolvedTable = new ResolvedTable();
		typeDelegatorTable = new TypeDelegatorTable();
	}
	
	public JarLoader getLoader() {
		return loader;
	}
	
	abstract protected void importJarStarted(Jar jar);
	abstract protected void importJarFinished(Jar jar);
	abstract protected void removeJarStarted(Jar jar);
	abstract protected void removeJarFinished(Jar jar);
	abstract protected void jarStateChanged(Jar jar, int state);

	private void validate(String uri) throws InvalidURIException {
		if (collection.contains(uri)) {
			throw new InvalidURIException(uri, "The jar has been already imported.");
		}
		if (uri.startsWith("http://") || uri.startsWith("file:///") || uri.startsWith("https://")) {
			try {
				new URI(uri);
			} catch (URISyntaxException e) {
				throw new InvalidURIException(uri, "Invalid format.");
			}
		} else {
			File file = new File(uri);
			if (!file.exists())
				throw new InvalidURIException(uri, "File not exist.");
		}
	}
	
	public final void importJar(String uri) throws InvalidURIException {
		this.importJar(uri, null);
	}
	
	/**
	 * This method runs concurrently.
	 * @throws InvalidURIException 
	 */
	public synchronized void importJar(final String uri, final ProgressMonitor monitor) throws InvalidURIException {
		validate(uri);
		queueImportJar.executeAndWait(new Runnable() {
			@Override
			public void run() {
				Jar jar = new Jar(uri);
				// realize listener
				Jar.StateChangeListener listener = new Jar.StateChangeListener() {
					@Override
					public void stateChange(Jar jar, int state) {
						jarStateChanged(jar, state);
					}
				};
				jar.setListener(listener);
				stateChangeListeners.add(listener);
				collection.add(jar);
				importJarStarted(jar);
				jar.setState(Jar.LOADING);
				analyzeConcurrently(jar, monitor);
			}
		});
	}

	private void analyzeConcurrently(final Jar jar, final ProgressMonitor monitor) {
		// step 1: download
		queueDownloading.execute(new Runnable() {
			@Override
			public void run() {
				try {
					final File file = loader.load(jar.getURI(), monitor);
					jar.setName(file.getName());
					
					// step 2 and 3: parse and build structure
					queueBuilding.execute(new Runnable() {
						@Override
						public void run() {
							try {
								
								// step 2: parse
								jar.setState(Jar.PARSING);
								final JarFile jarFile = new JarFile(file);
								final JavaClass[] javaClasses = parseClassesInJarFile(jarFile);
								final List<String[]> refsList = new ArrayList<String[]>();
								for (int i = 0; i < javaClasses.length; i++) {
									refsList.add(parseReference(javaClasses[i]));
								}
								
								// step 3: build structure
								jar.setState(Jar.BUILDING_STRUCTURE);
								final Type[] types = buildJarStructure(jar, javaClasses, monitor);
								
								// step 4: analyze dependency (not concurrently!)
								queueAnalysis.execute(new Runnable() {
									@Override
									public void run() {
										jar.setState(Jar.BUILDING_DEPENDENCY);
										buildDependency(javaClasses, types, refsList, monitor);
										
										if (monitor != null) monitor.finished();
										jar.setState(Jar.READY);
										importJarFinished(jar);
									}
								});
								
							} catch (IOException e) {
								jar.setException(e);
								jar.setState(Jar.ERROR);
							}
						}
					});
					
				} catch (IOException e) {
					jar.setName("Missing Jar");
					jar.setException(e);
					jar.setState(Jar.ERROR);
				}
			}
		});
	}
	
	/**
	 * Step 2 of Import
	 */
	private JavaClass[] parseClassesInJarFile(JarFile jarFile) throws IOException {
		final ArrayList<JavaClass> rst = new ArrayList<JavaClass>();
		// enumerate classes in jar
		Enumeration<JarEntry> it = jarFile.entries();
		while (it.hasMoreElements()) {
			JarEntry jarEntry = it.nextElement();
			if (jarEntry.getName().endsWith(".class")) {
				// parse class
				InputStream is = jarFile.getInputStream(jarEntry);
				ClassParser parser = new ClassParser(is, jarEntry.getName());
				JavaClass javaClass = parser.parse();
				rst.add(javaClass);
			}
		}
		return rst.toArray(new JavaClass[0]);
	}

	/**
	 * Step 3 of Import
	 */
	private Type[] buildJarStructure(Jar jar, JavaClass[] javaClasses, ProgressMonitor monitor) {
		final Type[] types = new Type[javaClasses.length];
		for (int i = 0; i < javaClasses.length; i++) {
			JavaClass javaClass = javaClasses[i];
			
			// create owner package
			String packageName = javaClass.getPackageName();
			Package p = jar.getPackage(packageName);
			if (p == null) {
				p = new Package();
				p.setName(packageName);
				jar.addPackage(p);
				p.setOwner(jar);
			}
			
			// create type
			Type type = javaClass.isInterface() ? new Interface() : new Class();
			types[i] = type;
			p.addType(type); // add type to package
			type.setOwner(p);
			String name = javaClass.getClassName();
			if (name.contains(".")) {
				name = name.substring(name.lastIndexOf('.') + 1);
			}
			type.setName(name);
			org.apache.bcel.classfile.Field[] fields = javaClass.getFields();
			org.apache.bcel.classfile.Method[] methods = javaClass.getMethods();
			
			// add field to type
			for (org.apache.bcel.classfile.Field field : fields) {
				Field f = new Field();
				f.setName(field.getName());
				org.apache.bcel.generic.Type fieldType = field.getType();
				if (fieldType instanceof org.apache.bcel.generic.ArrayType) {
					fieldType = ((org.apache.bcel.generic.ArrayType) fieldType).getBasicType();
					f.setArray(true);
				} else {
					f.setArray(false);
				}
				this.typeDelegatorTable.register(f, fieldType.toString());
				f.setOwner(type);
				type.addMember(f);
				setModifier(f, field);
			}
			
			// add methods to type
			for (org.apache.bcel.classfile.Method method : methods) {
				org.apache.bcel.generic.Type[] args = method.getArgumentTypes();
				Method m = new Method(args.length);
				m.setName(method.getName());
				org.apache.bcel.generic.Type retType = method.getReturnType();
				if (retType instanceof org.apache.bcel.generic.ArrayType) {
					retType = ((org.apache.bcel.generic.ArrayType) retType).getBasicType();
					m.setReturnArray(true);
				} else {
					m.setReturnArray(false);
				}
				this.typeDelegatorTable.register(m, retType.toString());
				
				for (int j = 0; j < args.length; j++) {
					org.apache.bcel.generic.Type argType = args[j];
					if (argType instanceof org.apache.bcel.generic.ArrayType) {
						argType = ((org.apache.bcel.generic.ArrayType) argType).getBasicType();
						m.setArgumentArray(true, j);
					} else {
						m.setArgumentArray(false, j);
					}
					this.typeDelegatorTable.register(m, argType.toString(), j);
				}
				m.setOwner(type);
				type.addMember(m);
				setModifier(m, method);
			}
			
			// set type property
			type.setAbstract(javaClass.isAbstract());
			type.setStatic(javaClass.isStatic());
			type.setFinal(javaClass.isFinal());
			int modifiers = javaClass.getModifiers();
			if ((modifiers & Constants.ACC_PUBLIC) != 0) {
				type.setAccessModifiers(IModifier.PUBLIC);
			} else if ((modifiers & Constants.ACC_PROTECTED) != 0) {
				type.setAccessModifiers(IModifier.PROTECTED);
			} else if ((modifiers & Constants.ACC_PRIVATE) != 0) {
				type.setAccessModifiers(IModifier.PRIVATE);
			} else {
				type.setAccessModifiers(IModifier.DEFAULT);
			}
			
			// update monitor
			int inv = javaClasses.length / 20 + 1;
			if (monitor != null && (i % inv == 0)) monitor.updateProgress((i + 1) / (double) javaClasses.length);
		}
		return types;
	}

	/**
	 * Step 4 of Import
	 */
	private void buildDependency(JavaClass[] javaClasses, Type[] types, List<String[]> refsList, ProgressMonitor monitor) {
		for (int i = 0; i < javaClasses.length; i++) {
			JavaClass javaClass = javaClasses[i];
			Type type = types[i];
			
			for (String ref : refsList.get(i)) {
				type.addUnresolvedReferenceName(ref);
				type.getOwner().addUnresolvedReferenceName(cn.edu.buaa.sei.jdat.Utility.getPackageName(ref));
				Type t = resolvedTable.getType(ref);
				if (t != null) {
					t.addInverseReference(type);
					type.addReference(t);
					// add package dependencies
					Package pkg = type.getOwner();
					Package pref = t.getOwner();
					if (pkg != pref) {
						pref.addInverseReference(pkg);
						pkg.addReference(pref);
						// add jar dependencies
						if (pkg.getOwner() != pref.getOwner()) {
							pref.getOwner().addInverseReference(pkg.getOwner());
							pkg.getOwner().addReference(pref.getOwner());
						}
					}
				} else {
					// update UnresolvedTable
					unresolvedTable.addItem(ref, type);
				}
			}
			
			// fill super-types into type
			type.addSupertypeName(javaClass.getSuperclassName());
			if (!javaClass.isInterface()) {
				((Class)type).setSuperclassName(javaClass.getSuperclassName());
			}
			String[] interfaceNames = javaClass.getInterfaceNames();
			for (String interfaceName : interfaceNames) {
				type.addSupertypeName(interfaceName);
			}
	
			// update ResolvedTable
			resolvedTable.addType(type);
			// update TypeDelegatorTable
			typeDelegatorTable.addType(type);
			
			// update UnresolvedTable
			List<Type> unresolvedRefType = unresolvedTable.getTypes(type.getSignature());
			if (unresolvedRefType != null) {
				for (Type t : unresolvedRefType) {
					t.addReference(type);
					type.addInverseReference(t);
					// add package dependencies
					Package pkg = t.getOwner();
					Package pref = type.getOwner();
					if (pkg != pref) {
						pref.addInverseReference(pkg);
						pkg.addReference(pref);
						// add jar dependencies
						if (pkg.getOwner() != pref.getOwner()) {
							pref.getOwner().addInverseReference(pkg.getOwner());
							pkg.getOwner().addReference(pref.getOwner());
						}
					}
				}
				unresolvedTable.removeName(type.getSignature());
			}
			
			// update monitor
			int inv = javaClasses.length / 20 + 1;
			if (monitor != null && (i % inv == 0)) monitor.updateProgress((i + 1) / (double) javaClasses.length);
		}
	}

	public void removeJar(Jar jar, ProgressMonitor monitor) {
		removeJarStarted(jar);
		deleteDependencies(jar, monitor);
		collection.remove(jar);
		removeJarFinished(jar);
	}
	
	public void removeJar(Jar jar) {
		removeJar(jar, null);
	}
	
	public Jar getJar(int index) {
		return collection.get(index);
	}
	
	public Jar[] getAllJars() {
		return collection.getAllJars();
	}
	
	public int indexOfJar(Jar jar) {
		return collection.indexOf(jar);
	}
	
	public Jar[] getJars(int[] indices) {
		int size = indices.length;
		Jar[] jars = new Jar[size];
		for (int i = 0; i < size; i++) {
			jars[i] = collection.get(indices[i]);
		}
		return jars;
	}
	
	public DependencyGraph generateGraph(Jar[] jars, int opt) {
		return new DependencyGraph(jars, opt);
	}
	
	public int getJarCount() {
		return collection.size();
	}
	
	private void setModifier(Member member, FieldOrMethod fm) {
		member.setAbstract(fm.isAbstract());
		member.setStatic(fm.isStatic());
		member.setFinal(fm.isFinal());
		int modifiers = fm.getModifiers();
		if ((modifiers & Constants.ACC_PUBLIC) != 0) {
			member.setAccessModifiers(IModifier.PUBLIC);
		} else if ((modifiers & Constants.ACC_PROTECTED) != 0) {
			member.setAccessModifiers(IModifier.PROTECTED);
		} else if ((modifiers & Constants.ACC_PRIVATE) != 0) {
			member.setAccessModifiers(IModifier.PRIVATE);
		} else {
			member.setAccessModifiers(IModifier.DEFAULT);
		}
	}
	
	private String[] parseReference(final JavaClass javaClass) {
		// XXX: enhance visitor
		class DependencyVisitor extends EmptyVisitor {
			
			final public ArrayList<String> strings = new ArrayList<String>();
			final public HashSet<String> reference = new HashSet<String>();
			
			public void visitConstantClass(ConstantClass cls) {
				String sCls = cls.getBytes(javaClass.getConstantPool());
				if (sCls.indexOf("/") != -1) {
					sCls = sCls.replace('/', '.');
					sCls = this.cleanClass(sCls);
					if (!reference.contains(sCls) && !sCls.equals(javaClass.getClassName())) {
						reference.add(sCls);
					}
				}
			}

			public void visitConstantUtf8(ConstantUtf8 utf) {
				String utfString = replace(utf.getBytes(), "\n", "\\n");
				if (isValidJavaClass(utfString)) {
					if (!this.strings.contains(utfString)) {
						String[] classes = this.separateClasses(utfString);
						for (int i = 0; i < classes.length; i++) {
							if (classes[i] != null) {
								String cls = classes[i];
								// String packageName = this.stripClassName(cls);
								cls = cls.replace('/', '.');
								cls = this.cleanClass(cls);
								if (cls.contains("<")) {
									cls = cls.substring(0, cls.indexOf("<"));
								}
								if (!reference.contains(cls) && !cls.equals(javaClass.getClassName())) {
									reference.add(cls);
								}
							}
						}
					}
				}
			}
			
			private final String replace(String str, String old, String newString) {
		        int index, old_index;
		        StringBuilder buf = new StringBuilder();
		        try {
		            if ((index = str.indexOf(old)) != -1) { // `old' found in str
		                old_index = 0; // String start offset
		                // While we have something to replace
		                while ((index = str.indexOf(old, old_index)) != -1) {
		                    buf.append(str.substring(old_index, index)); // append prefix
		                    buf.append(newString); // append replacement
		                    old_index = index + old.length(); // Skip `old'.length chars
		                }
		                buf.append(str.substring(old_index)); // append rest of string
		                str = buf.toString();
		            }
		        } catch (StringIndexOutOfBoundsException e) { // Should not occur
		            System.err.println(e);
		        }
		        return str;
		    }

			public void visitConstantString(ConstantString str) {
				this.strings.add(str.getBytes(javaClass.getConstantPool()).toString());
			}
			
			private boolean isValidJavaClass(String cls) {
				if (cls.indexOf("/") == -1) {
					return false;
				}
				if ( (!cls.startsWith("(")) && (!cls.startsWith("L")) ) {
					return false;
				}

				if ( (!cls.endsWith("V")) && (!cls.endsWith(";")) && (!cls.endsWith(")")) ) {
					return false;
				}

				return true;
			}

			private String[] separateClasses(String utfString) {
				StringTokenizer tokenizer = new StringTokenizer(utfString, ";");
				String classes[] = new String[tokenizer.countTokens()];
				int i = 0;
				while (tokenizer.hasMoreTokens()) {
					String cls = tokenizer.nextToken();
					if (cls.indexOf('/') != -1) {
						classes[i] = cls;
						i++;
					}
				}
				return classes;
			}

			private String cleanClass(String cls) {
				if (cls.startsWith("L")) return cls.substring(1);
				else if (cls.contains(".")) { // not in default package
					int index = cls.substring(0, cls.lastIndexOf(".")).lastIndexOf('L');
					if (index != -1) {
						return cls.substring(index + 1);
					}
				}
				return cls;
			}
			
		}
		
		DependencyVisitor refVisitor = new DependencyVisitor();
		DescendingVisitor descendingVisitor = new DescendingVisitor(javaClass, refVisitor);
		javaClass.accept(descendingVisitor);
		return refVisitor.reference.toArray(new String[0]);
	}
	
	private void deleteDependencies(Jar jar, ProgressMonitor monitor) {
		int allcount = 0;
		for (Package pkg : jar.getPackages()) {
			allcount += pkg.getTypes().size();
		}
		
		int count = 0;
		
		for (Package pkg : jar.getPackages()) {
			for (Type type : pkg.getTypes()) {
				// remove type in resolved table first, 
				// in order to get next type with same name
				resolvedTable.removeType(type);
				typeDelegatorTable.removeType(type);
				Type succ = resolvedTable.getType(type.getSignature());
				if (succ != null)
					typeDelegatorTable.addType(type);
				
				for (Type t : type.getReferences()) {
					t.removeInverseReference(type);
				}
				for (String unres : type.getUnresolvedReferences()) {
					// Update unresolved table.
					unresolvedTable.removeItem(unres, type);
				}
				for (Type t : type.getInverseReferences()) {
					t.removeReference(type);
					if (succ != null) {
						// move the dependencies to successor.
						succ.addInverseReference(t);
						t.addReference(succ);
					} else {
						// Update unresolved table.
						unresolvedTable.addItem(type.getSignature(), t);
					}
				}
				
				// update TypeDelegatorTable
				for (Member member : type.getMembers()) {
					if (member instanceof Field)
						typeDelegatorTable.unregister((Field) member);
					else
						typeDelegatorTable.unregister((Method) member);
				}
				
				type.destory();
				count++;
				monitor.updateProgress((double) count / allcount);
			}
			for (Package p : pkg.getReferences()) {
				p.removeInverseReference(pkg);
			}
			for (Package p : pkg.getInverseReferences()) {
				p.removeReference(pkg);
			}
			pkg.destory();
		}
		for (Jar j : jar.getReferences()) {
			j.removeInverseReference(jar);
		}
		for (Jar j : jar.getInverseReferences()) {
			j.removeReference(jar);
		}
		jar.destory();
		monitor.finished();
	}
}
